package org.example.seri

import com.alibaba.fastjson2.JSON
import com.baidu.bjf.remoting.protobuf.FieldType
import com.baidu.bjf.remoting.protobuf.ProtobufProxy
import com.baidu.bjf.remoting.protobuf.annotation.Protobuf
import com.baidu.bjf.remoting.protobuf.annotation.ProtobufClass
import com.esotericsoftware.kryo.Kryo
import com.esotericsoftware.kryo.SerializerFactory
import com.esotericsoftware.kryo.io.Input
import com.esotericsoftware.kryo.io.Output
import com.fasterxml.jackson.databind.json.JsonMapper
import com.fasterxml.jackson.databind.util.JSONPObject
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromByteArray
import kotlinx.serialization.encodeToByteArray
import kotlinx.serialization.json.Json
import kotlinx.serialization.protobuf.ProtoBuf
import kotlinx.serialization.protobuf.ProtoIntegerType
import kotlinx.serialization.protobuf.ProtoNumber
import kotlinx.serialization.protobuf.ProtoType
import redis.PersonOuterClass
import java.io.ByteArrayOutputStream


@Serializable
open class DataP @OptIn(ExperimentalSerializationApi::class) constructor(
    @ProtoType(ProtoIntegerType.DEFAULT)
    @ProtoNumber(10)
    val aa: Int,

    @ProtoType(ProtoIntegerType.DEFAULT)
    @ProtoNumber(11)
    val dd: String,
)

@Serializable
class Data @OptIn(ExperimentalSerializationApi::class) constructor(
    @ProtoType(ProtoIntegerType.DEFAULT)
    @ProtoNumber(1)
    var a: Int = 0,

    @ProtoType(ProtoIntegerType.SIGNED)
    @ProtoNumber(2)
    var b: Int = 0,

    @ProtoType(ProtoIntegerType.FIXED)
    @ProtoNumber(3)
    var c: Int = 0,

    @ProtoType(ProtoIntegerType.DEFAULT)
    @ProtoNumber(4)
    var d: String = "",

    @ProtoType(ProtoIntegerType.DEFAULT)
    @ProtoNumber(5)
    var l: List<String> = listOf(),

    @ProtoType(ProtoIntegerType.DEFAULT)
    @ProtoNumber(6)
    var m: Map<String, String> = mapOf(),
) : DataP(a, d)


@OptIn(ExperimentalSerializationApi::class)
fun main() {
    // 缓存codec, 小数据量也比protobuf原生的快，大数据量差不多0~5%以内
    val datajCodec = ProtobufProxy.create(Dataj::class.java)
    val num = 100_0000
    var pbSize = 0L
    var jsSize = 0L
    speedTest {
        // 17735  15969
        for (i in 1..2 * num) {
            val data = Dataj(
                111, 222, 3333, "hello world 卡霍夫卡换卡的说法是？",
                listOf("qw", "ddd", "retert"), mapOf("qw" to "qw", "ddd" to "ddd", "retert" to "retert")
            )
            val bytes = datajCodec.encode(data)
            val data1 = datajCodec.decode(bytes)
            pbSize += bytes.size
//        println(JSON.toJSONString(data1))
        }
    }
    /*speedTest {
        // 14339  15929
        for (i in 1..num) {
            val person = PersonOuterClass.Person.newBuilder()
                .setA(111).setB(222).setC(333).setD("hello world 卡霍夫卡换卡的说法是？")
                .addAllL(listOf("qw", "ddd", "retert"))
                .putAllM(mapOf("qw" to "qw", "ddd" to "ddd", "retert" to "retert"))
                .setAa(111).setDd("hello world 卡霍夫卡换卡的说法是？")
                .build()
            val bytes = person.toByteArray()
            val person2 = PersonOuterClass.Person.parseFrom(bytes)
        }
    }*/
    /*speedTest {
        // kotlin提供的proto序列化太差了, 3倍5倍，，直接out
        for (i in 1..num) {
            val data = Data(
                111, 222, 3333, "hello world 卡霍夫卡换卡的说法是？",
                listOf("qw", "ddd", "retert"), mapOf("qw" to "qw", "ddd" to "ddd", "retert" to "retert")
            )
            val bytes = ProtoBuf.encodeToByteArray(data)
            val data1 = ProtoBuf.decodeFromByteArray<Data>(bytes)
        }
    }*/
    speedTest {
        // 大数据1千万，速度比之Protobuf序列化 差近1倍，差 8s，小数据差40%左右
        for (i in 1..num) {
            val data = Data(
                111, 222, 3333, "hello world 卡霍夫卡换卡的说法是？",
                listOf("qw", "ddd", "retert"), mapOf("qw" to "qw", "ddd" to "ddd", "retert" to "retert")
            )
            val string = Json.encodeToString(data)
            val data1 = Json.decodeFromString<Data>(string)
            jsSize += string.encodeToByteArray().size
        }
    }
    /*val fjson = speedTest {
        // 1KW 17337 差2.7s
        for (i in 1..num) {
            val data = Dataj(
                111, 222, 3333, "hello world 卡霍夫卡换卡的说法是？",
                listOf("qw", "ddd", "retert"), mapOf("qw" to "qw", "ddd" to "ddd", "retert" to "retert")
            )
            val string = JSON.toJSONString(data)
            val data1 = JSON.parseObject(string, Dataj::class.java)
        }
    }*/
    /*val jackjson = speedTest {
        // 1KW 22060 比之fastjson慢 3997
        val objectMapper = jacksonObjectMapper()
        for (i in 1..num) {
            val data = Dataj(
                111, 222, 3333, "hello world 卡霍夫卡换卡的说法是？",
                listOf("qw", "ddd", "retert"), mapOf("qw" to "qw", "ddd" to "ddd", "retert" to "retert")
            )
            val s = objectMapper.writeValueAsString(data)
            val dataj = objectMapper.readValue<Dataj>(s)
        }
    }
    println(jackjson - fjson)*/

    /*val kryo = Kryo()
    val defaultFactory = SerializerFactory.CompatibleFieldSerializerFactory()
//    defaultFactory.config.chunkedEncoding = true // 启用分块编码，这个会降低性能，但能兼容类的删除。
    kryo.isRegistrationRequired = true
    kryo.setDefaultSerializer(defaultFactory)
    kryo.register(Dataj::class.java)
    kryo.register(DataPp::class.java)
    kryo.register(List::class.java)
    kryo.register(Map::class.java)
    kryo.register(java.util.Arrays::class.java)
    kryo.register(java.util.Arrays.asList("a").javaClass)
    kryo.register(java.util.LinkedHashMap::class.java)

    kryo.register(emptyList<String>().javaClass)
    kryo.register(emptyMap<String, String>().javaClass)
    kryo.register(emptySet<String>().javaClass)

    speedTest {
        // 19058 18058 稍慢
        for (i in 1..num) {
            val baos = KryoThreadLocalBuffers.baosThreadLocal.get()
            val output = KryoThreadLocalBuffers.outputThreadLocal.get()
            baos.reset()
            output.reset()

            val dataj = Dataj(
                111, 222, 3333, "hello world 卡霍夫卡换卡的说法是？",
                listOf("qw", "ddd", "retert"), mapOf("qw" to "qw", "ddd" to "ddd", "retert" to "retert")
            )
            kryo.writeObject(output, dataj)

            output.flush()
            val dbData = baos.toByteArray()

//            val input = KryoThreadLocalBuffers.inputThreadLocal.get()
//            input.setBuffer(dbData)
//            val dataj1 = kryo.readObject(input, Dataj::class.java)

            val dataj1 = Input(dbData).use { input ->
                val obj = kryo.readObject(input, Dataj::class.java)
                Dataj::class.java.cast(obj)
            }
//            println(JSON.toJSONString(dataj1))
        }
    }*/


    println("pbsize: $pbSize  jsSize: $jsSize ${pbSize.toDouble()/jsSize.toDouble()} ")
}

object KryoThreadLocalBuffers {
    private const val DEFAULT_BUFFER_SIZE = 4096

    val baosThreadLocal = ThreadLocal.withInitial { ByteArrayOutputStream(DEFAULT_BUFFER_SIZE) }
    val outputThreadLocal = ThreadLocal.withInitial { Output(baosThreadLocal.get()) }
    val inputThreadLocal = ThreadLocal.withInitial { Input(DEFAULT_BUFFER_SIZE) }
}
fun speedTest(action: () -> Unit) : Long {
    val l = System.currentTimeMillis()
    action()
    val l1 = System.currentTimeMillis() - l
    println(l1)
    return l1
}

@ProtobufClass
open class DataPp(
    @Protobuf(fieldType = FieldType.INT32, order = 10)
    var aa: Int,

    @Protobuf(fieldType = FieldType.STRING, order = 11)
    var dd: String
) {
    constructor() : this(aa = 0, dd = "")
}

@ProtobufClass
class Dataj(
    @Protobuf(fieldType = FieldType.INT32, order = 1)
    var a: Int,

    @Protobuf(fieldType = FieldType.SINT32, order = 2)
    var b: Int,

    @Protobuf(fieldType = FieldType.FIXED32, order = 3)
    var c: Int,

    @Protobuf(fieldType = FieldType.STRING, order = 4)
    var d: String,

    @Protobuf(fieldType = FieldType.STRING, order = 5)
    var l: List<String>,  // ✅ 用 List，不要用 Array

    @Protobuf(fieldType = FieldType.MAP, order = 6)
    var m: Map<String, String>, // ✅ jprotobuf 支持 Map

//    @Protobuf(fieldType = FieldType.OBJECT, order = 7)
//    var obj: Any = "",

) : DataPp(a, d) {
    constructor() : this(a = 0, b = 0, c = 0, d = "", l = listOf(), m = mapOf())
}


